#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "ttorrent.h"
#include "views.h"

#include <libtorrent/session.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/torrent_handle.hpp>

#include <libtorrent/asio/ip/address.hpp>

gboolean update_seed_row
(
 GtkTreeModel * model,
 GtkTreeIter * iter,
 TSettings * settings,
 libtorrent::torrent_handle * th,
 libtorrent::torrent_status * ts
)
{
  guint64 d0, d1, u0, u1;
  gfloat ratio;

  gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
    COL_T_DOWN_PREV, &d0,
    COL_T_DOWN_SESS, &d1,
    COL_T_UP_PREV, &u0,
    COL_T_UP_SESS, &u1,
  -1);

  ratio = (gfloat) (u0+u1)/(d0+d1+1);

  if( ratio < settings->seed_ratio ) return FALSE;

  gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_T_RATIO, ratio, -1);
  {
    th->pause();
    gtk_list_store_set (GTK_LIST_STORE (model), iter,
      COL_T_STATE, S_DONE,
      COL_T_SEEDS, -1, COL_T_PEERS, -1,
      COL_T_DOWN_SPEED, -1.0, COL_T_UP_SPEED,-1.0,
      COL_T_DOWN_SESS, ts->total_payload_download,
      COL_T_UP_SESS, ts->total_payload_upload,
      COL_T_RATIO, ratio,
      COL_T_PROGRESS, 100 * ts->progress,
    -1);
  }
  return TRUE;
}

void
update_peers_view(TTorrent * ttorrent)
{
    if(!GTK_WIDGET_DRAWABLE(ttorrent->peers_view)) return;

    libtorrent::torrent_handle * th = (libtorrent::torrent_handle *) ttorrent->handle;

    if(th == NULL) return;
    if(th->is_paused()) return;

    GtkTreeModel * model = gtk_tree_view_get_model(GTK_TREE_VIEW(ttorrent->peers_view));
    model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(model));
    GtkTreeIter iter;
    gboolean valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);

    std::vector<libtorrent::peer_info> * pi = new std::vector< libtorrent::peer_info> ();;
    std::vector<libtorrent::peer_info>::iterator pi_iter;
    th->get_peer_info(*pi);

    pi_iter = pi->begin();
    asio::ip::address ip1;
    asio::ip::address ip2;
    gchar *c_ip;

    while(TRUE)
    {
        if(valid && (pi_iter != pi->end()) )
        {
            if(pi_iter->flags & (0x40 | 0x80 | 0x100))
            {
              pi_iter++;
              continue;
            };
            gtk_tree_model_get(model, &iter, COL_P_IP, &c_ip, -1);
            ip1 = pi_iter->ip.address();
            ip2 = asio::ip::address::from_string(c_ip);
            if(ip1<ip2)
            {
              gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter, &iter);
              gtk_list_store_set(
                GTK_LIST_STORE (model), &iter,
                COL_P_IP, ip1.to_string().c_str(),
                COL_P_DOWN, pi_iter->down_speed / 1024,
                COL_P_UP, pi_iter->up_speed / 1024,
                COL_P_CLIENT, pi_iter->client.c_str(),
                COL_P_FLAGS, pi_iter->flags,
              -1);
              valid = gtk_tree_model_iter_next(model, &iter);
              ++pi_iter;
            }
            else if(ip2<ip1) valid=gtk_list_store_remove(GTK_LIST_STORE (model), &iter);
            else
            {
                  gtk_list_store_set(
                  GTK_LIST_STORE (model), &iter,
                  COL_P_DOWN, pi_iter->down_speed / 1024,
                  COL_P_UP, pi_iter->up_speed / 1024,
                  COL_P_FLAGS, pi_iter->flags,
                  -1);
                valid = gtk_tree_model_iter_next(model, &iter);
                ++pi_iter;
            };
            continue;
        }

        if(!valid)
        {
            while(pi_iter != pi->end())
            {
                if(pi_iter->flags & (0x40 | 0x80 | 0x100))
                {
                  pi_iter++;
                  continue;
                };
                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
                gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                  COL_P_IP, pi_iter->ip.address().to_string().c_str(),
                  COL_P_DOWN, pi_iter->down_speed / 1024,
                  COL_P_UP, pi_iter->up_speed / 1024,
                  COL_P_CLIENT, pi_iter->client.c_str(),
                  COL_P_FLAGS, pi_iter->flags,
                  -1);
                ++pi_iter;
            }
            break;
        }

        if(pi_iter == pi->end())
        {
            while(valid)
                valid = gtk_list_store_remove(GTK_LIST_STORE (model), &iter);
            break;
        }
    }

    delete pi;
}

bool lt_piece(libtorrent::partial_piece_info const& x, libtorrent::partial_piece_info const& y)
{
  return (x.piece_index < y.piece_index);
}

void
update_blocks_view(TTorrent * ttorrent)
{
    if(!GTK_WIDGET_DRAWABLE(ttorrent->blocks_view)) return;

    GtkTreeModel * sort_model = gtk_tree_view_get_model(GTK_TREE_VIEW(ttorrent->blocks_view));
    GtkTreeModel * model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(sort_model));

    GtkTreeIter iter;
    gboolean valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
    gint have;

    libtorrent::torrent_handle * th = (libtorrent::torrent_handle *) ttorrent->handle;
    if(th == NULL) return;

    libtorrent::torrent_status ts = th->status();
    std::vector<libtorrent::partial_piece_info> * ppi = new std::vector< libtorrent::partial_piece_info> ();
    std::vector<libtorrent::partial_piece_info>::iterator ppi_iter;

    th->get_download_queue(*ppi);

    std::sort(ppi->begin(), ppi->end(), lt_piece);
    gint p_index;

    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
    ppi_iter = ppi->begin();

    while(TRUE)
    {
        if(valid && (ppi_iter != ppi->end()) )
        {
            gtk_tree_model_get(model, &iter, COL_B_INDEX, &p_index, -1);

            if(p_index > ppi_iter->piece_index)
            {
              gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter, &iter);
              have = ppi_iter->finished_blocks.count();

              gtk_list_store_set(
                GTK_LIST_STORE (model), &iter,
                COL_B_INDEX, ppi_iter->piece_index,
                COL_B_HAVE, have,
                COL_B_MAX, ppi_iter->blocks_in_piece,
                COL_B_PROGRESS, (gfloat) 100 * have / ppi_iter->blocks_in_piece,
              -1);

              valid = gtk_tree_model_iter_next(model, &iter);
              ++ppi_iter;
            }
            else if(ppi_iter->piece_index > p_index)
              valid = gtk_list_store_remove(GTK_LIST_STORE (model), &iter);
            else
            {
              have = ppi_iter->finished_blocks.count();

              gtk_list_store_set(
                GTK_LIST_STORE (model), &iter,
                COL_B_HAVE, have,
                COL_B_PROGRESS, (gfloat) 100 * have / ppi_iter->blocks_in_piece,
              -1);
              valid = gtk_tree_model_iter_next(model, &iter);
              ++ppi_iter;
            };
            continue;
        }

        if(!valid)
        {
            while(ppi_iter != ppi->end())
            {
                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
                have = ppi_iter->finished_blocks.count();

                gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                  COL_B_INDEX, ppi_iter->piece_index,
                  COL_B_HAVE, have,
                  COL_B_MAX, ppi_iter->blocks_in_piece,
                  COL_B_PROGRESS, (gfloat) 100 * have / ppi_iter->blocks_in_piece,
                -1);
                ++ppi_iter;
            }
            break;
        }

        if(ppi_iter == ppi->end())
        {
            while(valid)
              valid = gtk_list_store_remove(GTK_LIST_STORE (model), &iter);
            break;
        }
    }

    delete ppi;
}

void
update_files_view(TTorrent * ttorrent)
{
    if(!GTK_WIDGET_DRAWABLE(ttorrent->files_view)) return;
    libtorrent::torrent_handle * th = (libtorrent::torrent_handle *) ttorrent->handle;
    if(th == NULL) return;

    GtkTreeModel * model = gtk_tree_view_get_model(GTK_TREE_VIEW(ttorrent->files_view));
    model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(model));
    GtkTreeIter iter;

    gboolean valid = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(model), &iter);
    if(!valid) return;
    gint * edited;
    gtk_tree_model_get(model, &iter, COL_F_EDITED, &edited, -1);
    if(*edited) return;

    std::vector<float> * fp = new std::vector<float>();
    th->file_progress(*fp);

    std::vector<float>::iterator fp_iter = fp->begin();
    while(fp_iter != fp->end())
    {
      gtk_list_store_set(GTK_LIST_STORE(model), &iter,
        COL_F_PROGRESS, 100 * (*fp_iter),
      -1);
      if (gtk_tree_model_iter_next(model, &iter)) break;
      ++fp_iter;
    }
    delete fp;
}

/* *****************************************************************
 * Public functions
 * ******************************************************************/

extern "C"
void main_loop (TTorrent * ttorrent)
{
    GdkWindowState state = gdk_window_get_state(ttorrent->window->window);

    if((state & GDK_WINDOW_STATE_ICONIFIED) == GDK_WINDOW_STATE_ICONIFIED)
      return;

    //GtkTreeModel *sort_model;
    GtkTreeModel *model;
    GtkTreeIter iter;
    gboolean valid;

    libtorrent::torrent_handle * th;
    libtorrent::torrent_status ts;

    model = gtk_tree_view_get_model(ttorrent->torrents_view);

    model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(model));
    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);

    gint active = 0;

    while (valid)
    {
        gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, COL_T_HANDLE, &th, -1);

        ts = th->status ();

        if ( th->is_seed() )
          if(update_seed_row(model, &iter, ttorrent->settings, th, &ts))
          {
            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
            continue;
          }

        if (th->is_paused())
        {
          if( active < ttorrent->settings->active_torrents )
          {
              guint state;
              gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
                COL_T_STATE, &state, -1);
              if (state != S_DONE)
              {
                ++active;
                th->resume();
                gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                  COL_T_STATE, S_ACTIVE, -1);
              }
          }
          valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
          continue;
        }

        if ( active >= ttorrent->settings->active_torrents )
        {
          th->pause();
          gtk_list_store_set (GTK_LIST_STORE (model), &iter,
            COL_T_STATE, S_PAUSED,
            COL_T_SEEDS, -1, COL_T_PEERS, -1,
            COL_T_DOWN_SPEED, -1.0, COL_T_UP_SPEED,-1.0,
            COL_T_DOWN_SESS, ts.total_payload_download,
            COL_T_UP_SESS, ts.total_payload_upload,
          -1);
          valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
          continue;
        }

        ++active;
        gtk_list_store_set (GTK_LIST_STORE (model), &iter,
          COL_T_SEEDS, ts.num_seeds,
          COL_T_SEEDS_SWARM, ts.num_complete,
          COL_T_PEERS, ts.num_peers,
          COL_T_PEERS_SWARM, ts.num_incomplete,
          COL_T_DOWN_SPEED, ts.download_payload_rate/1024,
          COL_T_UP_SPEED, ts.upload_payload_rate/1024,
          COL_T_DOWN_SESS, ts.total_payload_download,
          COL_T_UP_SESS, ts.total_payload_upload,
          COL_T_PROGRESS, 100 * ts.progress,
        -1);
        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
    }

    if (ttorrent->handle == 0) return;
    update_peers_view(ttorrent);
    update_files_view(ttorrent);
    update_blocks_view(ttorrent);
}

extern "C"
void query_session_status(gpointer sess, TStatus * ts)
{
    libtorrent::session_status ss = ((libtorrent::session *)sess) ->status();

    ts->down_speed = ss.payload_download_rate/1024;
    ts->up_speed = ss.payload_upload_rate/1024;
    ts->num_peers = ss.num_peers;
    ts->dht_nodes = ss.dht_nodes;
    ts->has_incoming = ss.has_incoming_connections;
}
